/*******************************************************************************
 * @file Copyright 2009 The CREATE Lab at CMU
 * 
 *       This file is part of JubJub, an open source project.
 *       http://code.google.com/p/jubjub/
 * 
 *       Redistribution and use in source and binary forms, with or without
 *       modification, are permitted provided that the following conditions are
 *       met:
 * 
 *       Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 * 
 *       Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 *       Neither the name of Carnegie Mellon nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 * 
 *       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *       HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

package edu.cmu.ri.createlab.jj.shapes;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.geom.GeneralPath;

import javax.swing.JFrame;
import javax.swing.JPanel;

import edu.cmu.ri.createlab.jj.blocks.BlockDefs.*;
import edu.cmu.ri.createlab.jj.visitors.BVDrawer;
import edu.cmu.ri.createlab.jj.shapes.JJShapeFactory.PathTypes;
import static edu.cmu.ri.createlab.jj.shapes.ShapeDefs.*;

///Produces a shape of the specified block type
public class JJShapeFactory
{
   
   
   public enum PathTypes
   {
      CLASS_TITLE,
      CLASS_METHOD,
      METHOD_TITLE,
      VARS_SPACE,
      METHOD_END,
      COMMENT_BLOCK,
      NORMAL_BLOCK,
      ASSIGN_BLOCK,
      OP_BLOCK,
      VAR_BLOCK,
      FLOW_TOP_BLOCK,
      FLOW_MID_BLOCK,
      FLOW_BOT_BLOCK,
      FLOW_IF_BLOCK,
      FLOW_ELSE_BLOCK,
      FLOW_IEBOT_BLOCK
   }
   
   private int top;
   private int bot;
   
   public BlockPath buildPath(int x, int y, PathTypes pt, int height, double scale)
   {
      BlockPath bp = new BlockPath();
      GeneralPath path;
      switch(pt)
      {
      case CLASS_TITLE:
         path = buildCTP(x,y,scale);
         break;
      case METHOD_TITLE:
         path = buildMTP(x,y,height,scale);
         break;
      case VARS_SPACE:
         path = buildVSP(x,y,height,scale);
         break;
      case METHOD_END:
         path = buildMEP(x,y,height,scale);
         break;
      case COMMENT_BLOCK:
         path = buildCBP(x,y,height,scale);
         break;
      case NORMAL_BLOCK:
         path = buildNBP(x,y,height,scale);
         break;
      case ASSIGN_BLOCK:
         path = buildABP(x,y,scale);
         break;
      case OP_BLOCK:
         path = buildOBP(x,y,scale);
         break;
      case VAR_BLOCK:
         path = buildVBP(x,y,scale);
         break;
      case FLOW_TOP_BLOCK:
      case FLOW_MID_BLOCK:
      case FLOW_BOT_BLOCK:
      case FLOW_IF_BLOCK:
      case FLOW_ELSE_BLOCK:
      case FLOW_IEBOT_BLOCK:
         path = buildFBP(x,y,scale, pt);
         break;
      default:
         path = null;
         top = 0;
         bot = 0;
         break;
      }
      bp.setPath(path);
      bp.setTop(top);
      bp.setBot(bot);
      //TODO add code to build a path depending on the type of block and height at x,y
      return bp;
   }
   
   public GeneralPath buildFBP(int x, int y, double s, PathTypes pt)
   {
      GeneralPath p = new GeneralPath();
      top = y;
      float scale = (float)s;
      if(pt == PathTypes.FLOW_TOP_BLOCK)
      {
         bot = (int) (y+flowTopPlaceHeight*scale);
         p.moveTo(x, y+tabHeight*scale);
         p.lineTo(x, y+flowTopHeight*scale);
         p.lineTo(x+indentWidth*scale, y+flowTopHeight*scale);
         p.lineTo(x+(indentWidth+tabWidth2)*scale, bot);
         p.lineTo(x+flowWidth*scale, bot);
         p.lineTo(x+flowWidth*scale, top);
         p.lineTo(x+tabWidth2*scale, top);
         p.lineTo(x, y+tabHeight*scale);
      }
      else if(pt == PathTypes.FLOW_MID_BLOCK)
      {
         bot = (int) (y+flowMidPlaceHeight*scale);
         p.moveTo(x, y);
         p.lineTo(x, y+flowMidHeight*scale);
         p.lineTo(x+indentWidth*scale, y+flowMidHeight*scale);
         p.lineTo(x+(indentWidth+tabWidth2)*scale, bot);
         p.lineTo(x+flowWidth*scale, bot);
         p.lineTo(x+flowWidth*scale, top);
         p.lineTo(x+(tabWidth2+indentWidth)*scale, top);
         p.lineTo(x+indentWidth*scale, y+tabHeight*scale);
         p.lineTo(x+indentWidth*scale, y);
         p.lineTo(x, y);
      }
      else if(pt == PathTypes.FLOW_BOT_BLOCK)
      {
         bot = (int) (y+flowBotPlaceHeight*scale);
         p.moveTo(x, y);
         p.lineTo(x, y+flowBotHeight*scale);
         p.lineTo(x+tabWidth1*scale, y+flowBotHeight*scale);
         p.lineTo(x+(tabWidth2)*scale, bot);
         p.lineTo(x+flowWidth*scale, bot);
         p.lineTo(x+flowWidth*scale, top);
         p.lineTo(x+(tabWidth2+indentWidth)*scale, top);
         p.lineTo(x+indentWidth*scale, y+tabHeight*scale);
         p.lineTo(x+indentWidth*scale, y);
         p.lineTo(x, y);
      }
      //TODO Finish if else paths
      else if(pt == PathTypes.FLOW_IF_BLOCK)
      {
         bot = (int) (y+flowTopPlaceHeight*scale);
         p.moveTo(x, y);
         p.lineTo(x, y+flowTopHeight*scale);
         p.lineTo(x+tabWidth1*scale, y+flowTopHeight*scale);
         p.lineTo(x+(tabWidth2)*scale, bot);
         p.lineTo(x+flowIfElseWidth*scale, bot);
         p.lineTo(x+flowIfElseWidth*scale, top);
         p.lineTo(x+(tabWidth2+indentWidth)*scale, top);
         p.lineTo(x+indentWidth*scale, y+tabHeight*scale);
         p.lineTo(x+indentWidth*scale, y);
         p.lineTo(x, y);
      }
      else if(pt == PathTypes.FLOW_ELSE_BLOCK)
      {
         bot = (int) (y+flowBotPlaceHeight*scale);
         p.moveTo(x, y);
         p.lineTo(x, y+flowBotHeight*scale);
         p.lineTo(x+tabWidth1*scale, y+flowBotHeight*scale);
         p.lineTo(x+(tabWidth2)*scale, bot);
         p.lineTo(x+flowWidth*scale, bot);
         p.lineTo(x+flowWidth*scale, top);
         p.lineTo(x+(tabWidth2+indentWidth)*scale, top);
         p.lineTo(x+indentWidth*scale, y+tabHeight*scale);
         p.lineTo(x+indentWidth*scale, y);
         p.lineTo(x, y);
      }
      else if(pt == PathTypes.FLOW_BOT_BLOCK)
      {
         bot = (int) (y+flowBotPlaceHeight*scale);
         p.moveTo(x, y);
         p.lineTo(x, y+flowBotHeight*scale);
         p.lineTo(x+tabWidth1*scale, y+flowBotHeight*scale);
         p.lineTo(x+(tabWidth2)*scale, bot);
         p.lineTo(x+flowWidth*scale, bot);
         p.lineTo(x+flowWidth*scale, top);
         p.lineTo(x+(tabWidth2+indentWidth)*scale, top);
         p.lineTo(x+indentWidth*scale, y+tabHeight*scale);
         p.lineTo(x+indentWidth*scale, y);
         p.lineTo(x, y);
      }
      return p;
   }
   
   ///Create a class title path
   private GeneralPath buildCTP(int x, int y, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)(cTitleHeight*scale);
      p.moveTo(x,y);
      p.lineTo(x+(cTitleHeight)*scale, y);
      p.lineTo(x+(cTitleHeight)*scale, y + cTitleWidth*scale);
      p.lineTo(x, y + cTitleWidth*scale);
      p.lineTo(x, y);
      return p;
   }

   ///Create a method title path
   private GeneralPath buildMTP(int x, int y, int height, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)(mTitleTopHeight*scale) + (int)((mTitleBotHeight-2)*scale)-(int)(tabHeight*scale);
      for(int i = 0; i <= height; i++)
         bot += (int)(middleHeight*scale);
      float yLowest = bot + (int)(tabHeight*scale);
      float yLow = bot;
      p.moveTo(x, y);
      p.lineTo(x, yLowest);
      p.lineTo(x+tabWidth1*scale, yLowest);
      p.lineTo(x+tabWidth2*scale, yLow);
      p.lineTo(x+mTitleWidth*scale, yLow);
      p.lineTo(x+mTitleWidth*scale, y);
      p.lineTo(x,y);
      return p;
   }

   ///Create a variable space path
   private GeneralPath buildVSP(int x, int y, int height, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)(varSpaceTopHeight*scale) + (int)(varSpaceBotHeight*scale) - (int)(tabHeight*scale);
      ///This is to make it match up with the graphics
      for(int i = 0; i <= height; i++)
         bot += (int)(middleHeight*scale);
      float yLowest = bot+(int)(tabHeight*scale);
      float yLow = bot;
      float yHigh = top+(int)(tabHeight*scale);
      
      p.moveTo(x, yHigh);
      p.lineTo(x,yLowest);
      p.lineTo(x+tabWidth1*scale, yLowest);
      p.lineTo(x+tabWidth2*scale, yLow);
      p.lineTo(x+varSpaceWidth*scale, yLow);
      p.lineTo(x+varSpaceWidth*scale, y);
      p.lineTo(x+tabWidth2*scale, y);
      p.lineTo(x+tabWidth1*scale, yHigh);
      p.lineTo(x, yHigh);
      return p;
   }

   ///Create a Method End path
   private GeneralPath buildMEP(int x, int y, int height, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)(mReturnHeight*scale);
      float yHigh = top+tabHeight*scale;
      p.moveTo(x, yHigh);
      p.lineTo(x, bot);
      p.lineTo(x+mTitleWidth*scale, bot);
      p.lineTo(x+mTitleWidth*scale, y);
      p.lineTo(x+tabWidth2*scale, y);
      p.lineTo(x+tabWidth1*scale, yHigh);
      p.lineTo(x, yHigh);
      return p;
   }

   ///Create a comment block path
   private GeneralPath buildCBP(int x, int y, int height, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)(topHeight*scale)+(int)(botHeight*scale)-(int)(tabHeight*scale);
      for(int i = 0; i < commentSize; i++)
         bot+= middleHeight*scale;
      float yLowest = bot + tabHeight*scale;
      float yLow = bot;
      float yHigh = top+tabHeight*scale;
      
      p.moveTo(x, yHigh);
      p.lineTo(x,yLowest);
      p.lineTo(x+tabWidth1*scale, yLowest);
      p.lineTo(x+tabWidth2*scale, yLow);
      p.lineTo(x+commentWidth*scale, yLow);
      p.lineTo(x+commentWidth*scale, y);
      p.lineTo(x+tabWidth2*scale, y);
      p.lineTo(x+tabWidth1*scale, yHigh);
      p.lineTo(x, yHigh);
      return p;
   }

   ///Create a Normal Block path
   private GeneralPath buildNBP(int x, int y, int height, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)(topHeight*scale) + (int)(botHeight*scale) - (int)(tabHeight*scale);
      for(int i = 0; i < height; i++)
         bot += (int)(middleHeight*scale);
      float yLowest = bot+tabHeight*scale;
      float yLow = bot;
      float yHigh = top+tabHeight*scale;
      
      p.moveTo(x, yHigh);
      p.lineTo(x,yLowest);
      p.lineTo(x+tabWidth1*scale, yLowest);
      p.lineTo(x+tabWidth2*scale, yLow);
      p.lineTo(x+normalWidth*scale, yLow);
      p.lineTo(x+normalWidth*scale, y);
      p.lineTo(x+tabWidth2*scale, y);
      p.lineTo(x+tabWidth1*scale, yHigh);
      p.lineTo(x, yHigh);
      return p;
   }

   ///Create an Assignment Block path
   private GeneralPath buildABP(int x, int y, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)((assignHeight-tabHeight)*scale);
      float yLowest = bot+tabHeight*scale;
      float yLow = bot;
      float yHigh = top+tabHeight*scale;
      
      p.moveTo(x, yHigh);
      p.lineTo(x,yLowest);
      p.lineTo(x+tabWidth1*scale, yLowest);
      p.lineTo(x+tabWidth2*scale, yLow);
      p.lineTo(x+assignWidth*scale, yLow);
      p.lineTo(x+assignWidth*scale, y);
      p.lineTo(x+(assignWidth+opWidth)*scale, y);
      p.lineTo(x+(assignWidth+opWidth)*scale, yLow);
      p.lineTo(x+assignWidth*scale, yLow);
      p.lineTo(x+assignWidth*scale, y);
      p.lineTo(x+tabWidth2*scale, y);
      p.lineTo(x+tabWidth1*scale, yHigh);
      p.lineTo(x, yHigh);
      return p;
   }

   ///Create a Operation Block path
   private GeneralPath buildOBP(int x, int y, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)((opHeight)*scale);
      p.moveTo(x,y);
      p.lineTo(x,bot);
      p.lineTo(x+opWidth*scale, bot);
      p.lineTo(x+opWidth*scale, top);
      p.lineTo(x, y);
      return p;
   }

   ///Create a Variable Block path
   private GeneralPath buildVBP(int x, int y, double s)
   {
      GeneralPath p = new GeneralPath();
      float scale = (float)s;
      top = y;
      bot = y + (int)((varHeight)*scale);
      p.moveTo(x,y);
      p.lineTo(x,bot);
      p.lineTo(x+varWidth*scale, bot);
      p.lineTo(x+varWidth*scale, top);
      p.lineTo(x, y);
      return p;
   }
   
   public static void main(String[] args)
   {
      JFrame frame = new JFrame("BVDrawer test");
      TestDrawingPanel panel = new TestDrawingPanel();
      Dimension psize = new Dimension(1024,728);
      panel.setPreferredSize(psize);
      
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(panel);
      
      frame.pack();
      
      frame.setVisible(true);
   }

}


class TestDrawingPanel extends JPanel
{
   JJShapeFactory sf;
   BlockPath bp1, vs1,cb1, nb1, vb1, vb2, ob1, ab1, ep1;
   
   TestDrawingPanel()
   {
      double scale = .75;
      sf = new JJShapeFactory();
      bp1 = sf.buildPath(10, 10, PathTypes.METHOD_TITLE, 2, scale);
      vs1 = sf.buildPath(10, bp1.getBot(), PathTypes.VARS_SPACE, 0, scale);
      cb1 = sf.buildPath(10, vs1.getBot(), PathTypes.COMMENT_BLOCK, 4, scale);
      nb1 = sf.buildPath(10, cb1.getBot(), PathTypes.NORMAL_BLOCK, 5, scale);
      ab1 = sf.buildPath(10, nb1.getBot(), PathTypes.ASSIGN_BLOCK, 0, scale);
      ob1 = sf.buildPath(10+(int)(295*scale), ab1.getTop(), PathTypes.OP_BLOCK,0,scale);
      vb1 = sf.buildPath(10+(int)((295+10)*scale), ab1.getTop()+(int)((6/2)*scale), PathTypes.VAR_BLOCK, 0, scale);
      ///180 is assignWidth, 120 is varWidth, 4 is minSpace
      vb2 = sf.buildPath(10+(int)((295+10+230+30)*scale), ab1.getTop()+(int)((6/2)*scale), PathTypes.VAR_BLOCK, 0, scale);
      ep1 = sf.buildPath(10, ab1.getBot(), PathTypes.METHOD_END, 0, scale);
   }
   
   public void paintComponent(Graphics g)
   {
      Graphics2D g2d = (Graphics2D)g;
      super.paintComponent(g);
      g2d.setColor(new Color(0,0,200));
      g2d.fill(bp1.getPath());
      g2d.setColor(new Color(200,200,200));
      g2d.fill(vs1.getPath());
      g2d.setColor(new Color(200,0,100));
      g2d.fill(cb1.getPath());
      g2d.setColor(new Color(100,100,255));
      g2d.fill(nb1.getPath());
      g2d.setColor(new Color(100,200,100));
      g2d.fill(ab1.getPath());
      g2d.setColor(new Color(100,220,100));
      g2d.fill(ob1.getPath());
      g2d.setColor(new Color(100,255,100));
      g2d.fill(vb1.getPath());
      g2d.fill(vb2.getPath());
      
      g2d.setColor(new Color(0,0,200));
      g2d.fill(ep1.getPath());
      g2d.setColor(new Color(200,0,0));
      g2d.draw(bp1.getPath());
      g2d.draw(vs1.getPath());
      g2d.draw(ep1.getPath());
   }
}

